/// ------------------------------------------------------------------------------------------------------------------------------------
///
/// MIT License
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all
/// copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
/// SOFTWARE.
///
/// Copyright (c) 2024 ycz. All rights reserved.
///
/// Created by ycz on 24-11-7.
///
/// @file  app_modbus.c
///
/// @brief
///     应用层 MODBUS 通信实现
///
/// ------------------------------------------------------------------------------------------------------------------------------------



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 头文件
/// ------------------------------------------------------------------------------------------------------------------------------------

#include "app_modbus.h"

#include <string.h>
#include "y_sa.h"
#include "app.h"



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 宏定义
/// ------------------------------------------------------------------------------------------------------------------------------------

#define MODBUS_REG_NUM 512  ///< modbus 寄存器数量



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 全局变量
/// ------------------------------------------------------------------------------------------------------------------------------------

static MODBUS_st *g_slave;                    ///< modbus 从机句柄
static uint8_t    g_reg[MODBUS_REG_NUM * 2];  ///< 从机寄存器 (uint8_t 好处理一点)
static UART_NUM_e g_uart_num = UART_6;        ///< modbus 使用的串口编号



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 私有函数
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   底层发送数据
/// @param   [in] data                     数据
/// @param   [in] size                     大小
/// @return  实际发送数据大小
static uint16_t _app_modbus_slave_write(uint8_t *data, uint16_t size) {
    return app_uart_send(g_uart_num, data, size);
}

/// @brief   从机设置寄存器
/// @param   [in] addr                     modbus 地址
/// @param   [in] reg                      寄存器地址
/// @param   [in] data                     寄存器数据
static bool _app_modbus_slave_reg_set(uint8_t addr, uint16_t reg, uint16_t data) {

    // 断言
    YLOGA_FALSE(reg < MODBUS_REG_NUM);

    // 指示灯 闪 100ms 指示正在通信
    // todo

    // 记录要设置的设备 mac
    static uint8_t mac[6] = {0};

    switch (reg) {

        // 系统模块 1-19 寄存器
        case 10: {
            memcpy(&mac[0], &data, 2);
        } break;
        case 11: {
            memcpy(&mac[2], &data, 2);
        } break;
        case 12: {
            memcpy(&mac[4], &data, 2);
        } break;

        case 13: {  // 设置通信地址

            // 判断是不是广播消息
            if (addr == 0 && y_utils_cmp_mac6(y_sys_get_mac(), mac) == false) {
                break;  // note: 广播消息 且 并不是设置本机 modbus 地址 则不处理
            } else {
                memset(mac, 0, 6);  // 每次设置后清空 mac缓存
            }

            if (data < 255 && data != 0) {
                y_sys_set_dev_num(data);
            }
        } break;

        case 14: {  // 设置波特率 note: 重启后生效
            uint32_t baudrate = 9600;
            if (data == 0) {
                baudrate = 4800;
            } else if (data == 1) {
                baudrate = 9600;
            } else if (data == 2) {
                baudrate = 19200;
            } else if (data == 3) {
                baudrate = 38400;
            } else if (data == 4) {
                baudrate = 57600;
            } else if (data == 5) {
                baudrate = 115200;
            } else if (data == 6) {
                baudrate = 128000;
            } else if (data == 7) {
                baudrate = 256000;
            } else if (data == 8) {
                baudrate = 460800;
            } else if (data == 9) {
                baudrate = 576000;
            } else if (data == 10) {
                baudrate = 921600;
            } else if (data == 11) {
                baudrate = 1000000;
            }
            app_uart_set_baudrate(g_uart_num, baudrate);
        } break;

        case 15: {  // 设置设备重启
            if (data) {
                memcpy(&g_reg[reg * 2], &data, 2);
                y_timer_set_reboot_time(1500, true);  // 1.5s 后重启设备
            }
        } break;
        case 16: {  // 设置设备重置
            if (data) {
                memcpy(&g_reg[reg * 2], &data, 2);
                y_nvs_reset();
                y_timer_set_reboot_time(1500, true);  // 1.5s 后重启设备
            }
        } break;


            // 属性模块 20 - n 寄存器


        default: {  // 升级 256-512 寄存器
            if (reg > 255) {
                static OTA_PACK_st pack            = {0};  // 帧数据结构体
                uint8_t            frame_head_size = sizeof(OTA_PACK_st) - sizeof(uint8_t *);
                memcpy(&g_reg[reg * 2], &data, 2);  // 保存到寄存器
                if (reg == 266) {                   // ota 帧头结束
                    memcpy(&pack, &g_reg[256 * 2], sizeof(OTA_PACK_st));
                    pack.data = &g_reg[(256 * 2) + frame_head_size];
                }
                if (pack.size && reg == 255 + (pack.size + frame_head_size) / 2) {
                    if (y_ota_upgrade(pack) == 100) {
                        y_timer_set_reboot_time(2000, true);  // 升级结束后 重启网关
                    }
                    memset(&pack, 0, sizeof(OTA_PACK_st));
                }
            }
        } break;
    }

    return true;
}

/// @brief   从机获取寄存器
/// @param   [in] reg                     寄存器地址
static uint16_t _app_modbus_slave_reg_get(uint16_t reg) {

    // 断言
    YLOGA_FALSE(reg < MODBUS_REG_NUM);

    // 指示灯 闪 100ms 指示正在通信
    // todo

    switch (reg) {

        // 系统模块 1-19 寄存器
        case 1: {  // 获取硬件版本号
            VERSION_st ver = y_sys_get_hardware_version();
            memcpy(&g_reg[reg * 2], &ver, 2);
        } break;
        case 2: {  // 获取硬件/软件版本号
            g_reg[2] = y_sys_get_hardware_version().patch;
            g_reg[3] = y_sys_get_software_version().major;
        } break;
        case 3: {  // 软件版本号
            VERSION_st ver = y_sys_get_software_version();
            memcpy(&g_reg[reg * 2], &ver.minor, 2);
        } break;

        case 4: {  // 获取设备类型
            uint16_t dev_type = y_sys_get_dev_type();
            memcpy(&g_reg[reg * 2], (uint8_t *) &dev_type, 2);
        } break;

        case 5: {  // 获取 sn
            memcpy(&g_reg[reg * 2], y_sys_get_sn(), 10);
        } break;

        case 10: {  // 获取 mac
            memcpy(&g_reg[reg * 2], y_sys_get_mac(), 6);
        } break;

        case 13: {  // 获取通信地址
            uint16_t modbus_addr = y_sys_get_dev_num();
            memcpy(&g_reg[reg * 2], (uint8_t *) &modbus_addr, 2);
        } break;

        case 14: {  // 获取波特率
            uint32_t baudrate = app_uart_get_baudrate(g_uart_num);
            if (baudrate == 4800) {
                baudrate = 0;
            } else if (baudrate == 9600) {
                baudrate = 1;
            } else if (baudrate == 19200) {
                baudrate = 2;
            } else if (baudrate == 38400) {
                baudrate = 3;
            } else if (baudrate == 57600) {
                baudrate = 4;
            } else if (baudrate == 115200) {
                baudrate = 5;
            } else if (baudrate == 128000) {
                baudrate = 6;
            } else if (baudrate == 256000) {
                baudrate = 7;
            } else if (baudrate == 460800) {
                baudrate = 8;
            } else if (baudrate == 576000) {
                baudrate = 9;
            } else if (baudrate == 921600) {
                baudrate = 10;
            } else if (baudrate == 1000000) {
                baudrate = 11;
            }
            memcpy(&g_reg[reg * 2], (uint8_t *) &baudrate, 2);
        } break;


            // 属性模块 20 - n 寄存器
    }

    return *(uint16_t *) &g_reg[reg * 2];
}



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 公有函数
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   打印 app_modbus 版本信息
void app_modbus_print_version() {
    YLOG_VERSION("app_modbus", APP_MODBUS_MAJOR, APP_MODBUS_MINOR, APP_MODBUS_PATCH);
}

/// @brief   获取从机句柄
/// @return  从机句柄
MODBUS_st *app_modbus_get_slave_handle() {
    return g_slave;
}

/// @brief   初始化
/// @retval  true                          成功
/// @retval  false                         失败
bool app_modbus_init() {

    // 从机初始化
    g_slave = y_modbus_init(MODBUS_SLAVE);
    YLOGA_FALSE(g_slave);
    g_slave->write   = _app_modbus_slave_write;
    g_slave->reg_set = _app_modbus_slave_reg_set;
    g_slave->reg_get = _app_modbus_slave_reg_get;

    return true;
}

/// @brief   modbus 任务处理
/// @param   [in] time_ms                  每次调用间隔时间
void app_modbus_process(uint32_t time_ms) {
    y_modbus_process(g_slave, time_ms);
}